package services

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"time"
)

// ScriptService 提供脚本管理功能
type ScriptService struct {
	scriptDir string
	processes map[string]*exec.Cmd
	mu        sync.RWMutex
}

// NewScriptService 创建一个新的脚本服务实例
func NewScriptService(scriptDir string) *ScriptService {
	return &ScriptService{
		scriptDir: scriptDir,
		processes: make(map[string]*exec.Cmd),
	}
}

// AddScript 添加新脚本
func (s *ScriptService) AddScript(name string, files map[string][]byte) error {
	scriptPath := filepath.Join(s.scriptDir, name)

	// 检查脚本是否已存在
	if _, err := os.Stat(scriptPath); err == nil {
		return fmt.Errorf("script '%s' already exists", name)
	}

	// 创建脚本目录
	if err := os.MkdirAll(scriptPath, 0755); err != nil {
		return fmt.Errorf("failed to create script directory: %v", err)
	}

	// 写入文件
	for filename, content := range files {
		filePath := filepath.Join(scriptPath, filename)
		if err := os.WriteFile(filePath, content, 0644); err != nil {
			os.RemoveAll(scriptPath) // 清理
			return fmt.Errorf("failed to write file '%s': %v", filename, err)
		}
	}

	return nil
}

// DeleteScript 删除脚本
func (s *ScriptService) DeleteScript(name string) error {
	scriptPath := filepath.Join(s.scriptDir, name)

	// 检查脚本是否存在
	if _, err := os.Stat(scriptPath); os.IsNotExist(err) {
		return fmt.Errorf("script '%s' does not exist", name)
	}

	// 如果脚本正在运行，先停止它
	if s.IsScriptRunning(name) {
		if err := s.StopScript(name); err != nil {
			return fmt.Errorf("failed to stop script before deletion: %v", err)
		}
	}

	// 删除脚本目录
	if err := os.RemoveAll(scriptPath); err != nil {
		return fmt.Errorf("failed to delete script: %v", err)
	}

	return nil
}

// RunScript 运行脚本
func (s *ScriptService) RunScript(name string, venvPath string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	// 检查脚本是否已经在运行
	if _, exists := s.processes[name]; exists {
		return fmt.Errorf("script '%s' is already running", name)
	}

	scriptPath := filepath.Join(s.scriptDir, name)
	mainFile := filepath.Join(scriptPath, "main.py")

	// 确定 Python 解释器路径
	pythonPath := "python"
	if venvPath != "" {
		if os.PathSeparator == '\\' { // Windows
			pythonPath = filepath.Join(venvPath, "Scripts", "python.exe")
		} else {
			pythonPath = filepath.Join(venvPath, "bin", "python")
		}
	}

	// 创建命令
	cmd := exec.Command(pythonPath, mainFile)
	cmd.Dir = scriptPath // 设置工作目录

	// 创建日志文件
	logFile, err := os.OpenFile(
		filepath.Join(scriptPath, "output.log"),
		os.O_CREATE|os.O_WRONLY|os.O_APPEND,
		0644,
	)
	if err != nil {
		return fmt.Errorf("failed to create log file: %v", err)
	}

	cmd.Stdout = logFile
	cmd.Stderr = logFile

	// 启动脚本
	if err := cmd.Start(); err != nil {
		logFile.Close()
		return fmt.Errorf("failed to start script: %v", err)
	}

	s.processes[name] = cmd

	// 在后台等待脚本结束
	go func() {
		cmd.Wait()
		s.mu.Lock()
		delete(s.processes, name)
		s.mu.Unlock()
		logFile.Close()
	}()

	return nil
}

// StopScript 停止脚本
func (s *ScriptService) StopScript(name string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	cmd, exists := s.processes[name]
	if !exists {
		return fmt.Errorf("script '%s' is not running", name)
	}

	// 在 Windows 上，我们需要使用 taskkill 来终止进程树
	if os.PathSeparator == '\\' {
		exec.Command("taskkill", "/F", "/T", "/PID", fmt.Sprint(cmd.Process.Pid)).Run()
	} else {
		if err := cmd.Process.Kill(); err != nil {
			return fmt.Errorf("failed to kill script process: %v", err)
		}
	}

	delete(s.processes, name)
	return nil
}

// GetScriptStatus 获取脚本状态
func (s *ScriptService) GetScriptStatus(name string) string {
	s.mu.RLock()
	defer s.mu.RUnlock()

	if _, exists := s.processes[name]; exists {
		return "Running"
	}
	return "Stopped"
}

// IsScriptRunning 检查脚本是否正在运行
func (s *ScriptService) IsScriptRunning(name string) bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	_, exists := s.processes[name]
	return exists
}

// GetRunningScripts 获取正在运行的脚本数量
func (s *ScriptService) GetRunningScripts() int {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return len(s.processes)
}

// GetScript 获取脚本信息
func (s *ScriptService) GetScript(name string) (*ScriptInfo, error) {
	scriptPath := filepath.Join(s.scriptDir, name)

	// 检查脚本是否存在
	info, err := os.Stat(scriptPath)
	if os.IsNotExist(err) {
		return nil, fmt.Errorf("script '%s' does not exist", name)
	}

	// 获取文件列表
	files, err := s.ListScriptFiles(name)
	if err != nil {
		return nil, err
	}

	// 查找主文件
	mainFile := "main.py"
	for _, file := range files {
		if file == "main.py" {
			break
		}
	}

	return &ScriptInfo{
		Name:      name,
		MainFile:  mainFile,
		Files:     files,
		CreatedAt: info.ModTime(),
		Status:    s.GetScriptStatus(name),
	}, nil
}

// ListScripts 列出所有脚本
func (s *ScriptService) ListScripts() ([]ScriptInfo, error) {
	entries, err := os.ReadDir(s.scriptDir)
	if err != nil {
		return nil, fmt.Errorf("failed to read script directory: %v", err)
	}

	var scripts []ScriptInfo
	for _, entry := range entries {
		if entry.IsDir() {
			info, err := s.GetScript(entry.Name())
			if err != nil {
				continue // 跳过无效的脚本
			}
			scripts = append(scripts, *info)
		}
	}

	return scripts, nil
}

// ListScriptFiles 列出脚本的所有文件
func (s *ScriptService) ListScriptFiles(name string) ([]string, error) {
	scriptPath := filepath.Join(s.scriptDir, name)

	entries, err := os.ReadDir(scriptPath)
	if err != nil {
		return nil, fmt.Errorf("failed to read script directory: %v", err)
	}

	var files []string
	for _, entry := range entries {
		if !entry.IsDir() {
			files = append(files, entry.Name())
		}
	}

	return files, nil
}

// ReadFile 读取脚本文件内容
func (s *ScriptService) ReadFile(name, filename string) ([]byte, error) {
	filePath := filepath.Join(s.scriptDir, name, filename)

	content, err := os.ReadFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read file: %v", err)
	}

	return content, nil
}

// WriteFile 写入脚本文件内容
func (s *ScriptService) WriteFile(name, filename string, content []byte) error {
	filePath := filepath.Join(s.scriptDir, name, filename)

	if err := os.WriteFile(filePath, content, 0644); err != nil {
		return fmt.Errorf("failed to write file: %v", err)
	}

	return nil
}

// CreateZip 创建脚本的压缩包
func (s *ScriptService) CreateZip(name string) (string, error) {
	scriptPath := filepath.Join(s.scriptDir, name)
	zipPath := scriptPath + ".zip"

	zipFile, err := os.Create(zipPath)
	if err != nil {
		return "", fmt.Errorf("failed to create zip file: %v", err)
	}
	defer zipFile.Close()

	archive := zip.NewWriter(zipFile)
	defer archive.Close()

	// 遍历脚本目录
	err = filepath.Walk(scriptPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 跳过目录本身
		if path == scriptPath {
			return nil
		}

		// 创建zip文件头
		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}

		// 设置相对路径
		header.Name, err = filepath.Rel(scriptPath, path)
		if err != nil {
			return err
		}

		// 创建文件
		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}

		// 如果是目录，不需要复制内容
		if info.IsDir() {
			return nil
		}

		// 复制文件内容
		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()

		_, err = io.Copy(writer, file)
		return err
	})

	if err != nil {
		os.Remove(zipPath)
		return "", fmt.Errorf("failed to create zip archive: %v", err)
	}

	return zipPath, nil
}

// ScriptInfo 脚本信息
type ScriptInfo struct {
	Name      string    `json:"name"`
	MainFile  string    `json:"main_file"`
	Files     []string  `json:"files"`
	CreatedAt time.Time `json:"created_at"`
	Status    string    `json:"status"`
}
